// Copyright Epic Games, Inc. All Rights Reserved.

#include "Teams/LyraTeamSubsystem.h"

#include "AbilitySystemGlobals.h"
#include "GameFramework/Controller.h"
#include "GameFramework/Pawn.h"
#include "LyraLogChannels.h"
#include "LyraTeamAgentInterface.h"
#include "LyraTeamCheats.h"
#include "LyraTeamPrivateInfo.h"
#include "LyraTeamPublicInfo.h"
#include "Player/LyraPlayerState.h"
#include "Teams/LyraTeamInfoBase.h"

#include UE_INLINE_GENERATED_CPP_BY_NAME(LyraTeamSubsystem)

class FSubsystemCollectionBase;

//////////////////////////////////////////////////////////////////////
// FLyraTeamTrackingInfo

/**
 * 设置团队信息
 */
void FLyraTeamTrackingInfo::SetTeamInfo(ALyraTeamInfoBase* Info)
{
	if (ALyraTeamPublicInfo* NewPublicInfo = Cast<ALyraTeamPublicInfo>(Info)) // 如果是公共团队信息
	{
		ensure((PublicInfo == nullptr) || (PublicInfo == NewPublicInfo)); // 确保公共信息为空或相同
		PublicInfo = NewPublicInfo; // 设置公共信息

		ULyraTeamDisplayAsset* OldDisplayAsset = DisplayAsset; // 保存旧显示资源
		DisplayAsset = NewPublicInfo->GetTeamDisplayAsset(); // 获取新显示资源

		if (OldDisplayAsset != DisplayAsset) // 如果显示资源发生变化
		{
			OnTeamDisplayAssetChanged.Broadcast(DisplayAsset); // 广播显示资源变化
		}
	}
	else if (ALyraTeamPrivateInfo* NewPrivateInfo = Cast<ALyraTeamPrivateInfo>(Info)) // 如果是私有团队信息
	{
		ensure((PrivateInfo == nullptr) || (PrivateInfo == NewPrivateInfo)); // 确保私有信息为空或相同
		PrivateInfo = NewPrivateInfo; // 设置私有信息
	}
	else
	{
		checkf(false, TEXT("Expected a public or private team info but got %s"), *GetPathNameSafe(Info)) // 检查失败，期望公共或私有团队信息
	}
}

/**
 * 移除团队信息
 */
void FLyraTeamTrackingInfo::RemoveTeamInfo(ALyraTeamInfoBase* Info)
{
	if (PublicInfo == Info) // 如果是公共信息
	{
		PublicInfo = nullptr; // 清空公共信息
	}
	else if (PrivateInfo == Info) // 如果是私有信息
	{
		PrivateInfo = nullptr; // 清空私有信息
	}
	else
	{
		ensureMsgf(false, TEXT("Expected a previously registered team info but got %s"), *GetPathNameSafe(Info)); // 确保失败，期望先前注册的团队信息
	}
}

//////////////////////////////////////////////////////////////////////
// ULyraTeamSubsystem

/**
 * 构造函数
 */
ULyraTeamSubsystem::ULyraTeamSubsystem()
{
}

/**
 * 初始化子系统
 */
void ULyraTeamSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
	Super::Initialize(Collection); // 调用父类初始化
	
	auto AddTeamCheats = [](UCheatManager* CheatManager) // 添加团队作弊命令的lambda
	{
		CheatManager->AddCheatManagerExtension(NewObject<ULyraTeamCheats>(CheatManager)); // 添加团队作弊管理器扩展
	};

	CheatManagerRegistrationHandle = UCheatManager::RegisterForOnCheatManagerCreated(FOnCheatManagerCreated::FDelegate::CreateLambda(AddTeamCheats)); // 注册作弊管理器创建回调
}

/**
 * 反初始化子系统
 */
void ULyraTeamSubsystem::Deinitialize()
{
	UCheatManager::UnregisterFromOnCheatManagerCreated(CheatManagerRegistrationHandle); // 取消注册作弊管理器创建回调

	Super::Deinitialize(); // 调用父类反初始化
}

/**
 * 注册团队信息
 */
bool ULyraTeamSubsystem::RegisterTeamInfo(ALyraTeamInfoBase* TeamInfo)
{
	if (!ensure(TeamInfo)) // 确保团队信息有效
	{
		return false; // 返回失败
	}

	const int32 TeamId = TeamInfo->GetTeamId(); // 获取团队ID
	if (ensure(TeamId != INDEX_NONE)) // 确保团队ID有效
	{
		FLyraTeamTrackingInfo& Entry = TeamMap.FindOrAdd(TeamId); // 查找或添加团队跟踪信息
		Entry.SetTeamInfo(TeamInfo); // 设置团队信息

		return true; // 返回成功
	}

	return false; // 返回失败
}

/**
 * 取消注册团队信息
 */
bool ULyraTeamSubsystem::UnregisterTeamInfo(ALyraTeamInfoBase* TeamInfo)
{
	if (!ensure(TeamInfo)) // 确保团队信息有效
	{
		return false; // 返回失败
	}

	const int32 TeamId = TeamInfo->GetTeamId(); // 获取团队ID
	if (ensure(TeamId != INDEX_NONE)) // 确保团队ID有效
	{
		FLyraTeamTrackingInfo* Entry = TeamMap.Find(TeamId); // 查找团队跟踪信息

		// 如果找不到条目，这可能是来自先前世界的残留角色，忽略它
		if (Entry) // 如果找到条目
		{
			Entry->RemoveTeamInfo(TeamInfo); // 移除团队信息

			return true; // 返回成功
		}
	}

	return false; // 返回失败
}

/**
 * 改变角色的团队
 */
bool ULyraTeamSubsystem::ChangeTeamForActor(AActor* ActorToChange, int32 NewTeamIndex)
{
	const FGenericTeamId NewTeamID = IntegerToGenericTeamId(NewTeamIndex); // 转换新团队索引为通用团队ID
	if (ALyraPlayerState* LyraPS = const_cast<ALyraPlayerState*>(FindPlayerStateFromActor(ActorToChange))) // 查找玩家状态
	{
		LyraPS->SetGenericTeamId(NewTeamID); // 设置通用团队ID
		return true; // 返回成功
	}
	else if (ILyraTeamAgentInterface* TeamActor = Cast<ILyraTeamAgentInterface>(ActorToChange)) // 如果是团队角色接口
	{
		TeamActor->SetGenericTeamId(NewTeamID); // 设置通用团队ID
		return true; // 返回成功
	}
	else
	{
		return false; // 返回失败
	}
}

/**
 * 从对象查找团队
 */
int32 ULyraTeamSubsystem::FindTeamFromObject(const UObject* TestObject) const
{
	// 查看它是否直接是团队代理
	if (const ILyraTeamAgentInterface* ObjectWithTeamInterface = Cast<ILyraTeamAgentInterface>(TestObject)) // 如果对象有团队接口
	{
		return GenericTeamIdToInteger(ObjectWithTeamInterface->GetGenericTeamId()); // 返回团队ID整数
	}

	if (const AActor* TestActor = Cast<const AActor>(TestObject)) // 如果是角色
	{
		// 查看发起者是否是团队角色
		if (const ILyraTeamAgentInterface* InstigatorWithTeamInterface = Cast<ILyraTeamAgentInterface>(TestActor->GetInstigator())) // 如果发起者有团队接口
		{
			return GenericTeamIdToInteger(InstigatorWithTeamInterface->GetGenericTeamId()); // 返回发起者团队ID整数
		}

		// TeamInfo角色实际上没有团队接口，所以它们需要特殊处理
		if (const ALyraTeamInfoBase* TeamInfo = Cast<ALyraTeamInfoBase>(TestActor)) // 如果是团队信息
		{
			return TeamInfo->GetTeamId(); // 返回团队ID
		}

		// 回退到查找关联的玩家状态
		if (const ALyraPlayerState* LyraPS = FindPlayerStateFromActor(TestActor)) // 查找玩家状态
		{
			return LyraPS->GetTeamId(); // 返回玩家团队ID
		}
	}

	return INDEX_NONE; // 返回无团队
}

/**
 * 从角色查找玩家状态
 */
const ALyraPlayerState* ULyraTeamSubsystem::FindPlayerStateFromActor(const AActor* PossibleTeamActor) const
{
	if (PossibleTeamActor != nullptr) // 如果可能团队角色有效
	{
		if (const APawn* Pawn = Cast<const APawn>(PossibleTeamActor)) // 如果是Pawn
		{
			//@TODO: 考虑使用接口，或者让团队角色向子系统注册并维护映射？（或LWC样式）
			if (ALyraPlayerState* LyraPS = Pawn->GetPlayerState<ALyraPlayerState>()) // 获取玩家状态
			{
				return LyraPS; // 返回玩家状态
			}
		}
		else if (const AController* PC = Cast<const AController>(PossibleTeamActor)) // 如果是控制器
		{
			if (ALyraPlayerState* LyraPS = Cast<ALyraPlayerState>(PC->PlayerState)) // 获取玩家状态
			{
				return LyraPS; // 返回玩家状态
			}
		}
		else if (const ALyraPlayerState* LyraPS = Cast<const ALyraPlayerState>(PossibleTeamActor)) // 如果是玩家状态
		{
			return LyraPS;  // 返回玩家状态
		}

		// 尝试发起者
// 		if (AActor* Instigator = PossibleTeamActor->GetInstigator()) // 获取发起者
// 		{
// 			if (ensure(Instigator != PossibleTeamActor)) // 确保发起者不是自身
// 			{
// 				return FindPlayerStateFromActor(Instigator); // 从发起者查找玩家状态
// 			}
// 		}
	}

	return nullptr; // 返回空
}

/**
 * 比较团队
 */
ELyraTeamComparison ULyraTeamSubsystem::CompareTeams(const UObject* A, const UObject* B, int32& TeamIdA, int32& TeamIdB) const
{
	TeamIdA = FindTeamFromObject(Cast<const AActor>(A)); // 查找A的团队ID
	TeamIdB = FindTeamFromObject(Cast<const AActor>(B)); // 查找B的团队ID

	if ((TeamIdA == INDEX_NONE) || (TeamIdB == INDEX_NONE)) // 如果任一团队ID无效
	{
		return ELyraTeamComparison::InvalidArgument; // 返回无效参数
	}
	else
	{
		return (TeamIdA == TeamIdB) ? ELyraTeamComparison::OnSameTeam : ELyraTeamComparison::DifferentTeams; // 返回团队比较结果
	}
}

/**
 * 比较团队（简化版）
 */
ELyraTeamComparison ULyraTeamSubsystem::CompareTeams(const UObject* A, const UObject* B) const
{
	int32 TeamIdA; // 团队ID A
	int32 TeamIdB; // 团队ID B
	return CompareTeams(A, B, /*out*/ TeamIdA, /*out*/ TeamIdB); // 调用完整比较方法
}

/**
 * 从角色查找团队
 */
void ULyraTeamSubsystem::FindTeamFromActor(const UObject* TestObject, bool& bIsPartOfTeam, int32& TeamId) const
{
	TeamId = FindTeamFromObject(TestObject); // 从对象查找团队ID
	bIsPartOfTeam = TeamId != INDEX_NONE; // 设置是否属于团队
}

/**
 * 添加团队标签堆栈
 */
void ULyraTeamSubsystem::AddTeamTagStack(int32 TeamId, FGameplayTag Tag, int32 StackCount)
{
	auto FailureHandler = [&](const FString& ErrorMessage) // 失败处理lambda
	{
		UE_LOG(LogLyraTeams, Error, TEXT("AddTeamTagStack(TeamId: %d, Tag: %s, StackCount: %d) %s"), TeamId, *Tag.ToString(), StackCount, *ErrorMessage); // 记录错误日志
	};

	if (FLyraTeamTrackingInfo* Entry = TeamMap.Find(TeamId)) // 查找团队跟踪信息
	{
		if (Entry->PublicInfo) // 如果公共信息存在
		{
			if (Entry->PublicInfo->HasAuthority()) // 如果有权限
			{
				Entry->PublicInfo->TeamTags.AddStack(Tag, StackCount); // 添加标签堆栈
			}
			else
			{
				FailureHandler(TEXT("failed because it was called on a client")); // 失败处理：在客户端调用
			}
		}
		else
		{
			FailureHandler(TEXT("failed because there is no team info spawned yet (called too early, before the experience was ready)")); // 失败处理：团队信息尚未生成
		}
	}
	else
	{
		FailureHandler(TEXT("failed because it was passed an unknown team id")); // 失败处理：未知团队ID
	}
}

/**
 * 移除团队标签堆栈
 */
void ULyraTeamSubsystem::RemoveTeamTagStack(int32 TeamId, FGameplayTag Tag, int32 StackCount)
{
	auto FailureHandler = [&](const FString& ErrorMessage) // 失败处理lambda
	{
		UE_LOG(LogLyraTeams, Error, TEXT("RemoveTeamTagStack(TeamId: %d, Tag: %s, StackCount: %d) %s"), TeamId, *Tag.ToString(), StackCount, *ErrorMessage); // 记录错误日志
	};

	if (FLyraTeamTrackingInfo* Entry = TeamMap.Find(TeamId)) // 查找团队跟踪信息
	{
		if (Entry->PublicInfo) // 如果公共信息存在
		{
			if (Entry->PublicInfo->HasAuthority()) // 如果有权限
			{
				Entry->PublicInfo->TeamTags.RemoveStack(Tag, StackCount); // 移除标签堆栈
			}
			else
			{
				FailureHandler(TEXT("failed because it was called on a client")); // 失败处理：在客户端调用
			}
		}
		else
		{
			FailureHandler(TEXT("failed because there is no team info spawned yet (called too early, before the experience was ready)")); // 失败处理：团队信息尚未生成
		}
	}
	else
	{
		FailureHandler(TEXT("failed because it was passed an unknown team id")); // 失败处理：未知团队ID
	}
}

/**
 * 获取团队标签堆栈计数
 */
int32 ULyraTeamSubsystem::GetTeamTagStackCount(int32 TeamId, FGameplayTag Tag) const
{
	if (const FLyraTeamTrackingInfo* Entry = TeamMap.Find(TeamId)) // 查找团队跟踪信息
	{
		const int32 PublicStackCount = (Entry->PublicInfo != nullptr) ? Entry->PublicInfo->TeamTags.GetStackCount(Tag) : 0; // 获取公共堆栈计数
		const int32 PrivateStackCount = (Entry->PrivateInfo != nullptr) ? Entry->PrivateInfo->TeamTags.GetStackCount(Tag) : 0; // 获取私有堆栈计数
		return PublicStackCount + PrivateStackCount; // 返回总堆栈计数
	}
	else
	{
		UE_LOG(LogLyraTeams, Verbose, TEXT("GetTeamTagStackCount(TeamId: %d, Tag: %s) failed because it was passed an unknown team id"), TeamId, *Tag.ToString()); // 记录详细日志
		return 0; // 返回0
	}
}

/**
 * 团队是否有标签
 */
bool ULyraTeamSubsystem::TeamHasTag(int32 TeamId, FGameplayTag Tag) const
{
	return GetTeamTagStackCount(TeamId, Tag) > 0; // 返回堆栈计数是否大于0
}

/**
 * 团队是否存在
 */
bool ULyraTeamSubsystem::DoesTeamExist(int32 TeamId) const
{
	return TeamMap.Contains(TeamId); // 返回团队映射是否包含团队ID
}

/**
 * 获取团队ID列表
 */
TArray<int32> ULyraTeamSubsystem::GetTeamIDs() const
{
	TArray<int32> Result; // 结果数组
	TeamMap.GenerateKeyArray(Result); // 生成键数组
	Result.Sort(); // 排序
	return Result; // 返回结果
}

/**
 * 是否可以造成伤害
 */
bool ULyraTeamSubsystem::CanCauseDamage(const UObject* Instigator, const UObject* Target, bool bAllowDamageToSelf) const
{
	if (bAllowDamageToSelf) // 如果允许对自身造成伤害
	{
		if ((Instigator == Target) || (FindPlayerStateFromActor(Cast<AActor>(Instigator)) == FindPlayerStateFromActor(Cast<AActor>(Target)))) // 如果是同一对象或同一玩家状态
		{
			return true; // 返回可以造成伤害
		}
	}

	int32 InstigatorTeamId; // 发起者团队ID
	int32 TargetTeamId; // 目标团队ID
	const ELyraTeamComparison Relationship = CompareTeams(Instigator, Target, /*out*/ InstigatorTeamId, /*out*/ TargetTeamId); // 比较团队关系
	if (Relationship == ELyraTeamComparison::DifferentTeams) // 如果是不同团队
	{
		return true; // 返回可以造成伤害
	}
	else if ((Relationship == ELyraTeamComparison::InvalidArgument) && (InstigatorTeamId != INDEX_NONE)) // 如果是无效参数且发起者有团队
	{
		// 暂时允许对非团队角色造成伤害，只要他们有技能系统组件
		//@TODO: 这是临时的，直到目标练习假人有团队分配
		return UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(Cast<const AActor>(Target)) != nullptr; // 返回目标是否有技能系统组件
	}

	return false; // 返回不可以造成伤害
}

/**
 * 获取团队显示资源
 */
ULyraTeamDisplayAsset* ULyraTeamSubsystem::GetTeamDisplayAsset(int32 TeamId, int32 ViewerTeamId)
{
	// 目前忽略ViewerTeamId

	if (FLyraTeamTrackingInfo* Entry = TeamMap.Find(TeamId)) // 查找团队跟踪信息
	{
		return Entry->DisplayAsset; // 返回显示资源
	}

	return nullptr; // 返回空
}

/**
 * 获取有效团队显示资源
 */
ULyraTeamDisplayAsset* ULyraTeamSubsystem::GetEffectiveTeamDisplayAsset(int32 TeamId, UObject* ViewerTeamAgent)
{
	return GetTeamDisplayAsset(TeamId, FindTeamFromObject(ViewerTeamAgent)); // 获取团队显示资源
}

/**
 * 通知团队显示资源已修改
 */
void ULyraTeamSubsystem::NotifyTeamDisplayAssetModified(ULyraTeamDisplayAsset* /*ModifiedAsset*/)
{
	// 当显示资源被编辑时，目前广播给所有观察者，而不是仅编辑的那个
	for (const auto& KVP : TeamMap) // 遍历团队映射
	{
		const int32 TeamId = KVP.Key; // 获取团队ID
		const FLyraTeamTrackingInfo& TrackingInfo = KVP.Value; // 获取团队跟踪信息

		TrackingInfo.OnTeamDisplayAssetChanged.Broadcast(TrackingInfo.DisplayAsset); // 广播显示资源变化
	}
}

/**
 * 获取团队显示资源变化委托
 */
FOnLyraTeamDisplayAssetChangedDelegate& ULyraTeamSubsystem::GetTeamDisplayAssetChangedDelegate(int32 TeamId)
{
	return TeamMap.FindOrAdd(TeamId).OnTeamDisplayAssetChanged; // 返回团队显示资源变化委托
}